﻿using System;
using System.Threading;
using System.Net.Sockets;
using AccessControl.Video.Rtp;
using System.Collections.Generic;
using System.Net;
using System.Runtime.InteropServices;
using System.IO;

namespace AccessControl.Video.Rtsp
{
	/// <summary>
	/// Manages RTCP socket
	/// </summary>
	/// <remarks></remarks>
	class RtcpSocket : SocketBase
	{
		#region Fields
		
		UdpClient _socket;
		Thread _socketThread;
		uint _highestSequenceNumber, _lastSSRC;
		bool _running, _gotSenderReport, _gotRtpPacket;

		#endregion

		#region Public Properties

		public int ServerRtcpPort { get; set; }
		public IPAddress ServerAddress { get; set; }

		#endregion

		#region Public Members

		/// <summary>
		/// Initializes this instance.
		/// </summary>
		/// <returns></returns>
		/// <remarks></remarks>
		public override bool Initialize()
		{
			/* Validate the socket */
			if (_socket != null)
				return false;

			/* Create it */
			try { _socket = new UdpClient(PortNumber); }
			catch
			{
				System.Diagnostics.Debug.WriteLine("Failed to create socket");
				return false;
			}

			/* And start the socket thread */
			_running = true;
			_socketThread = new Thread(SocketThread) { IsBackground = true };
			_socketThread.Start();

			return true;
		}

		/// <summary>
		/// Handles the new RTP packet.
		/// </summary>
		/// <param name="packet">The packet.</param>
		/// <remarks></remarks>
		public void HandleNewRtpPacket(RtpPacket packet)
		{
			/* Save the data */
			_lastSSRC = packet.SSRC;
			_highestSequenceNumber = packet.SequenceNumber;
			_gotRtpPacket = true;
		}

		/// <summary>
		/// Terminates this instance.
		/// </summary>
		/// <returns></returns>
		/// <remarks></remarks>
		public override bool Terminate()
		{
            _running = false;
            _socketThread.Join();
            _socketThread = null;

            try { _socket.Close(); }
            catch { }

            return true;
		}

		#endregion

		#region Private Members

		void SocketThread()
		{
			byte[] aBytes = new byte[2048];
			List<Socket> read = new List<Socket>();
			int nRead, nLastReportSent = Environment.TickCount;

			while (_running)
			{
				read.Clear();
				read.Add(_socket.Client);

				Socket.Select(read, null, null, 100 * 1000);
				foreach (Socket socket in read)
				{
					aBytes = new byte[2048];
					nRead = socket.Receive(aBytes);
					byte[] rtcpPacketBytes = new byte[nRead];
					Array.Copy(aBytes, rtcpPacketBytes, nRead);			
					HandleRtcpData(rtcpPacketBytes);
				}

				if (((Environment.TickCount - nLastReportSent) > 2000) && _gotRtpPacket)
				{
					SendReceiverReport();
					nLastReportSent = Environment.TickCount;
				}
			}
		}

		private void HandleRtcpData(byte [] rtcpBytes)
		{
			int size = Marshal.SizeOf(typeof(RtcpPacketHeader));
			IntPtr ptrPacketHeader = Marshal.AllocHGlobal(size);
			Marshal.Copy(rtcpBytes, 0, ptrPacketHeader, size);
			RtcpPacketHeader packetHeader = (RtcpPacketHeader)Marshal.PtrToStructure(ptrPacketHeader, typeof(RtcpPacketHeader));
			Marshal.FreeHGlobal(ptrPacketHeader);					
		}

		private void SendReceiverReport()
		{
			RtcpPacketHeader header = new RtcpPacketHeader();
			RtcpReceiverReport rr = new RtcpReceiverReport();

			header.Version = 2;
			header.Type = 201;
			header.Count = 1;
			header.Length = 7;

			rr.ReporterSSRC = 1234;
			rr.ReporteeSSRC = _lastSSRC;
			rr.Extended = _highestSequenceNumber;

			/* Get the bytes */
			byte[] headerBytes = header.ToBytes();
			byte[] rrBytes = rr.ToBytes();
			byte[] packet = new byte[headerBytes.Length + rrBytes.Length];
			Array.Copy(headerBytes, 0, packet, 0, headerBytes.Length);
			Array.Copy(rrBytes, 0, packet, headerBytes.Length, rrBytes.Length);
			/* And send it */
			_socket.Client.SendTo(packet, new IPEndPoint(ServerAddress, ServerRtcpPort));
		}

		#endregion
	}
}
